package impl

import (
	"sort"

	"hundsun.com/hsl/hschain/common/types"

	pb "hundsun.com/hsl/hschain/protos/execute"
	"hundsun.com/hsl/hschain/protos/ledger"
)

func getMergeGroups(groups map[int32][]int32) map[int32][]int32 {
	//list all vec
	vecs := make(map[int32]int32)
	for id, conf := range groups {
		if _, ok := vecs[id]; !ok {
			vecs[id] = id
		}
		for _, c := range conf {
			if _, ok := vecs[c]; !ok {
				vecs[c] = c
			}
		}
	}
	//dfs
	count := 0
	visited := make(map[int32]bool)
	for _, v := range vecs {
		visited[v] = false
	}
	vecNumber := len(vecs)
	grps := make(map[int32]int32)
	top := -1
	stack := make([]int32, vecNumber)
	for vec := range vecs {
		if visited[vec] {
			continue
		}
		visited[vec] = true
		v := vec
		grps[v] = int32(count)
		for {
			cfs := groups[v]
			for _, cf := range cfs {
				if !visited[cf] {
					visited[cf] = true
					top++
					stack[top] = cf
					grps[cf] = int32(count)
				}
			}
			if top < 0 {
				break
			}
			v = stack[top]
			top--
		}
		count++
	}

	rst := make(map[int32][]int32)
	for i := 0; i < count; i++ {
		r := make([]int32, 0)
		for _, v := range vecs {
			if grps[v] == int32(i) {
				r = append(r, v)
			}
		}
		rst[int32(i)] = r
	}
	return rst
}

//合并交易分组
func mergeGroups(groups []*pb.TransactionGroup, groupIds []int32) *pb.TransactionGroup {
	if len(groups) == 0 || len(groupIds) == 0 {
		return nil
	}
	//groupId大的合并到小的分组
	sort.Slice(groupIds, func(i, j int) bool {
		return groupIds[i] < groupIds[j]
	})
	leastID := groupIds[0]
	//合并分组
	mpGroups := make(map[int32]*pb.TransactionGroup)
	for _, group := range groups {
		mpGroups[group.GroupId] = group
	}

	leastGrp := &pb.TransactionGroup{GroupId: leastID}
	for _, grpID := range groupIds {
		if group, ok := mpGroups[grpID]; ok {
			leastGrp.Txs = append(leastGrp.Txs, group.Txs...)
		}
	}

	//排序交易
	for i := 0; i < len(leastGrp.Txs); i++ {
		// 交易中的GroupId置位
		leastGrp.Txs[i].GroupId = leastID
		//发生冲突交易的执行结果已经没有作用，需要清除
		leastGrp.Txs[i].TxExecRst = nil
		// 有可能会产生的中间地址也清除
		leastGrp.Txs[i].VarsRWStatus = genTxDefaultAddressRWStatus(leastGrp.Txs[i].GetTx())
	}
	sort.Slice(leastGrp.Txs, func(i, j int) bool {
		return leastGrp.Txs[i].TxIndex < leastGrp.Txs[j].TxIndex
	})
	return resetFirstConflictTxIndex(leastGrp)
}

func genTxDefaultAddressRWStatus(tx *ledger.Transaction) []*pb.RWStatus {
	var addRwStatus []*pb.RWStatus
	if tx.GetFrom() != "" {
		addRwStatus = append(addRwStatus, &pb.RWStatus{Key: []byte(tx.From), RwStatus: 1})
	}
	if tx.GetTo() != "" {
		addRwStatus = append(addRwStatus, &pb.RWStatus{Key: []byte(tx.To), RwStatus: 1})
	}
	return addRwStatus
}

func group(ltxs []*LocationTx) []*pb.TransactionGroup {
	addressToTxMap := make(map[string]int32)
	mpGroups := make(map[int32]*pb.TransactionGroup)
	groupID := int32(0)
	for _, ltx := range ltxs {
		tx := ltx.tx
		index := ltx.index
		addRwStatus := genTxDefaultAddressRWStatus(tx)
		gpTx := &pb.GroupedTransaction{
			Tx:           tx,
			TxIndex:      int32(index),
			GroupId:      groupID,
			VarsRWStatus: addRwStatus,
		}
		gpIDFm, okFm := addressToTxMap[tx.From]
		gpIDTo, okTo := addressToTxMap[tx.To]
		if !okFm && !okTo {
			gpTx.GroupId = groupID
			gp := &pb.TransactionGroup{
				GroupId:              groupID,
				Txs:                  []*pb.GroupedTransaction{gpTx},
				FirstConflictTxIndex: -1,
			}
			addressToTxMap[tx.From] = groupID
			addressToTxMap[tx.To] = groupID
			mpGroups[groupID] = gp
			groupID++
		} else if okFm && !okTo {
			gpTx.GroupId = gpIDFm
			addressToTxMap[tx.To] = gpIDFm
			gp := mpGroups[gpIDFm]
			gp.Txs = append(gp.Txs, gpTx)
			mpGroups[gpIDFm] = gp
		} else if !okFm && okTo {
			gpTx.GroupId = gpIDTo
			addressToTxMap[tx.From] = gpIDTo
			gp := mpGroups[gpIDTo]
			gp.Txs = append(gp.Txs, gpTx)
			mpGroups[gpIDTo] = gp
		} else { // okFm && okTo
			if gpIDFm == gpIDTo {
				gpTx.GroupId = gpIDTo
				gp := mpGroups[gpIDTo]
				gp.Txs = append(gp.Txs, gpTx)
				mpGroups[gpIDTo] = gp
			} else { // 合并分组
				min := gpIDFm
				max := gpIDTo
				if min > max {
					min, max = max, min
				}
				minGp := mpGroups[min]
				maxGp := mpGroups[max]
				for _, gpTx := range maxGp.Txs {
					gpTx.GroupId = min
					minGp.Txs = append(minGp.Txs, gpTx)
					// updata addressToTxMap
					addressToTxMap[gpTx.Tx.From] = min
					addressToTxMap[gpTx.Tx.To] = min
				}
				// add new
				gpTx.GroupId = min
				minGp.Txs = append(minGp.Txs, gpTx)
				// del
				delete(mpGroups, max)
				addressToTxMap[tx.From] = min
				addressToTxMap[tx.To] = min
			}
		}
	}
	rstGroup := make([]*pb.TransactionGroup, len(mpGroups))
	index := 0
	for _, gp := range mpGroups {
		rstGroup[index] = gp
		index++
	}
	sort.Slice(rstGroup, func(i, j int) bool {
		return rstGroup[i].GroupId < rstGroup[j].GroupId
	})
	return rstGroup
}

//交易重分组
//如果有冲突，返回新的交易分组和pendingGroups
func regroup(inGroups []*pb.TransactionGroup) (bool, []*pb.TransactionGroup, []*pb.GroupedTransaction, []*pb.TransactionGroup) {
	groups := cloneGroups(inGroups)
	addrMap := make(map[string][]*types.AddessGroupInfo)
	conflictMap := make(map[int32][]int32)
	groupedTxs := make([]*pb.GroupedTransaction, 0)
	for _, grp := range groups {
		groupedTxs = append(groupedTxs, grp.Txs...)
	}
	//将所有分组的所有交易展开，并按index从小到大排序
	sort.Slice(groupedTxs, func(i, j int) bool {
		return groupedTxs[i].TxIndex < groupedTxs[j].TxIndex
	})
	mixConflictIndex := int32(0xFFFF)
	for _, tx := range groupedTxs {
		txIndex := tx.TxIndex
		for _, txaddr := range tx.VarsRWStatus {
			txaddress := string(txaddr.Key)
			rwstat := txaddr.RwStatus
			//地址已经在之前的交易中出现过
			if addrInfo, ok := addrMap[txaddress]; ok {
				for _, a := range addrInfo {
					if a.GroupID == tx.GroupId { //检查之前的交易是否在同一个分组，如果在的话不冲突
						continue
					} else {
						//检查读写冲突
						//1.如果之前的交易是只读，不冲突
						//2.如果之前的交易是Write,都有冲突
						if a.RwStatus == 1 {
							//conflict
							conflictGroups := conflictMap[tx.GroupId]
							conflictGroups = append(conflictGroups, a.GroupID)
							conflictMap[tx.GroupId] = conflictGroups

							conflictGroups = conflictMap[a.GroupID]
							conflictGroups = append(conflictGroups, tx.GroupId)
							conflictMap[a.GroupID] = conflictGroups

							// 更新全局最小index
							index := txIndex
							if a.TxIndex < index {
								index = a.TxIndex
							}
							if mixConflictIndex > index {
								mixConflictIndex = index
							}
						}
					}
				}
				//记录地址
				addrGroupInfo := addrMap[txaddress]
				addrGroupInfo = append(addrGroupInfo, &types.AddessGroupInfo{GroupID: tx.GroupId, TxIndex: txIndex, RwStatus: int32(rwstat)})
				addrMap[txaddress] = addrGroupInfo
			} else {
				//新建地址
				addrMap[txaddress] = []*types.AddessGroupInfo{
					{
						GroupID:  tx.GroupId,
						TxIndex:  txIndex,
						RwStatus: int32(rwstat),
					},
				}
			}
		}
	}

	if len(conflictMap) == 0 {
		return false, nil, nil, nil
	}
	// 获取采纳交易
	accpetedTxs := make([]*pb.GroupedTransaction, 0)
	for i, grp := range groups {
		index := 0
		for _, grpTx := range grp.Txs {
			if grpTx.TxIndex <= mixConflictIndex {
				accpetedTxs = append(accpetedTxs, grpTx)
			} else {
				break
			}
			index++
		}
		if index >= len(grp.Txs) {
			groups[i].Txs = nil
		} else {
			groups[i].Txs = groups[i].Txs[index:]
		}
	}
	sort.Slice(accpetedTxs, func(i, j int) bool {
		return accpetedTxs[i].TxIndex < accpetedTxs[j].TxIndex
	})

	//合并冲突的交易分组
	mgs := getMergeGroups(conflictMap)
	conflictGroupIds := make(map[int32]struct{})
	newGroups := make([]*pb.TransactionGroup, 0)
	for _, mergeGrp := range mgs {
		mergedGrp := mergeGroups(groups, mergeGrp)
		newGroups = append(newGroups, mergedGrp)
		for _, mgrp := range mergeGrp {
			conflictGroupIds[mgrp] = struct{}{}
		}
	}

	//标记pending交易分组
	pendingGrps := make([]*pb.TransactionGroup, 0)
	for _, group := range groups {
		if len(group.Txs) == 0 { // 说明该分组交易全部采纳
			continue
		}
		if _, ok := conflictGroupIds[group.GroupId]; !ok { //不在冲突分组ID列表中的为pending分组
			pendingGrps = append(pendingGrps, resetFirstConflictTxIndex(group))
		}
	}

	hasConflict := false
	if len(conflictMap) > 0 {
		hasConflict = true
	}
	return hasConflict, newGroups, accpetedTxs, pendingGrps
}

func resetFirstConflictTxIndex(group *pb.TransactionGroup) *pb.TransactionGroup {
	if group != nil {
		group.FirstConflictTxIndex = -1
	}
	return group
}

// 浅拷贝
func cloneGroups(groups []*pb.TransactionGroup) []*pb.TransactionGroup {
	var cloneGroups []*pb.TransactionGroup
	for _, grp := range groups {
		cloneGroup := *grp
		cloneGroups = append(cloneGroups, &cloneGroup)
	}
	return cloneGroups
}
